package com.hyzh.latte.vehicle.nezha;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.hyzh.latte.util.MapKits;
import com.hyzh.latte.vehicle.dmo.entity.CameraPGEntity;
import com.hyzh.latte.vehicle.dmo.entity.GatePGEntity;
import com.hyzh.latte.vehicle.dmo.entity.PolePGEntity;
import com.mxgraph.layout.mxCircleLayout;
import com.mxgraph.layout.mxIGraphLayout;
import com.mxgraph.util.mxCellRenderer;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.jgrapht.GraphPath;
import org.jgrapht.alg.shortestpath.BFSShortestPath;
import org.jgrapht.ext.JGraphXAdapter;
import org.jgrapht.graph.DefaultDirectedGraph;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.List;

/**
 * @author songkui
 * @since 2024/3/19 16:04
 */

@Getter
@Slf4j
public class ParkPoleGraph {

    private DefaultDirectedGraph<PoleVertex, PoleEdge> poleGraph = null;
    private Map<String, PoleVertex> poleMap = null;  //key PoleCode_lab
    private Map<String, GatePGEntity> gateMap = null; // key gateNo
    private Map<String, PolePGEntity> poleEntityMap = null; //key poleCode
    private final String parkCode;

    private Map<String, CameraPGEntity> cameraMap = null;  // key: cameraCode;  v: camera;

    public ParkPoleGraph(String pakCode) {
        this.parkCode = pakCode;
    }


    // 如果需要修改； 则需要重新初始化
    public void initializePoleGraph(List<PoleEdge> poleEdgeList, Map<String, CameraPGEntity> cmap, Map<String, GatePGEntity> gm, Map<String, PolePGEntity> poleEntityMap) throws Exception {
        this.poleGraph = new DefaultDirectedGraph<>(PoleEdge.class);
        this.cameraMap = cmap;
        this.gateMap = gm;
        this.poleEntityMap = poleEntityMap;

        if (CollectionUtil.isEmpty(poleEdgeList)) {
            throw new Exception("Initialize pole graph  data is incomplete");
        }

        log.info("InitializePoleGraph: park: {}; cameraMap size: {}; poleEntityMap size: {};  poleEdgeList size: {};  gateMap size: {}; ",
                                           this.parkCode, this.cameraMap.size(), this.poleEntityMap.size(), poleEdgeList.size(), this.gateMap.size());

        Set<PoleVertex> poleList = new HashSet<>();
        for (PoleEdge poleEdge : poleEdgeList) {
            poleList.add(poleEdge.buildSourceVertex());
            poleList.add(poleEdge.buildTargetVertex());
        }

        poleMap = new HashMap<>(MapKits.mapSize(poleList.size()));
        for (PoleVertex vertex : poleList) {
            poleGraph.addVertex(vertex);
            poleMap.put(vertex.getPoleCode() + "_" + vertex.getLab(), vertex);
        }

        for (PoleEdge edge : poleEdgeList) {
            PoleVertex source = poleMap.get(edge.getSourcePoleCode() + "_" + edge.getSourceLab());
            PoleVertex target = poleMap.get(edge.getTargetPoleCode() + "_" + edge.getTargetLab());
            if (null == source || null == target) {
                throw new Exception("Initialize camera graph  edge  source  or target is null");
            }
            poleGraph.addEdge(source, target, edge);

        }

        Set<PoleVertex> vertexSet = poleGraph.vertexSet();
        if (CollectionUtil.isEmpty(vertexSet)) {
            throw new Exception("Initialize camera graph vertexSet  is null");
        }

        for (PoleVertex vertex : vertexSet) {
            vertex.setFork(isFork(vertex));
        }

    }

    public void updateCamera(Map<String, CameraPGEntity> map) {
        cameraMap = map;
    }

    private boolean isFork(PoleVertex start) {
        Set<PoleEdge> outgoingEdges = poleGraph.outgoingEdgesOf(start);
        return outgoingEdges.size() > 2 || (outgoingEdges.size() == 2 && outgoingEdges.stream().allMatch(edge -> edge.getDirection() != -1));
    }


    //当前节点 边
    public Set<PoleEdge> findEdgeByCurrent(PoleVertex start) {
        return poleGraph.outgoingEdgesOf(start);
    }



    public PoleVertex getNextNode(PoleVertex start) {
        Set<PoleEdge> outgoingEdges = poleGraph.outgoingEdgesOf(start);
        if (CollectionUtil.isNotEmpty(outgoingEdges)) {
            PoleEdge edge = outgoingEdges.stream().filter(e -> e.getDirection() != -1).findFirst().orElse(null);
            return null != edge ? poleGraph.getEdgeTarget(edge) : null;
        }
        return null;
    }
    public PoleVertex getNextNodeByDit(PoleVertex start, int dit) {
        Set<PoleEdge> outgoingEdges = poleGraph.outgoingEdgesOf(start);
        if (CollectionUtil.isNotEmpty(outgoingEdges)) {
            PoleEdge edge = outgoingEdges.stream().filter(e -> e.getDirection() == dit).findFirst().orElse(null);
            return null != edge ? poleGraph.getEdgeTarget(edge) : null;
        }
        return null;
    }


    //最少节点
    public List<PoleVertex> findVertexPaths(PoleVertex start, PoleVertex end) {
        try {
            BFSShortestPath<PoleVertex, PoleEdge> alg = new BFSShortestPath<>(poleGraph);
            GraphPath<PoleVertex, PoleEdge> path = alg.getPath(start, end);
            return null != path ? path.getVertexList() : null;
        } catch (Exception e) {
           log.error("findVertexPaths error>> start: {}, end: {}", start, end);
            return null;
        }
    }

    //最短边
    public List<PoleEdge> findEdgePaths(PoleVertex start, PoleVertex end) {
        BFSShortestPath<PoleVertex, PoleEdge> alg = new BFSShortestPath<>(poleGraph);
        return alg.getPath(start, end).getEdgeList();
    }


    //导出图片
    public void buildImage() throws IOException {
        JGraphXAdapter<PoleVertex, PoleEdge> graphAdapter = new JGraphXAdapter<>(poleGraph);
        mxIGraphLayout layout = new mxCircleLayout(graphAdapter);
        layout.execute(graphAdapter.getDefaultParent());

        BufferedImage image = mxCellRenderer.createBufferedImage(graphAdapter, null, 2, Color.WHITE, true, null);
        File imgFile = new File("D:/graph2.png");
        ImageIO.write(image, "PNG", imgFile);
    }

    //    首先 get camera   跟camera 属性 + 算法方向  判断 车辆 车道
    public PoleVertex  getPoleVertexByCameraCode(String cameraCode, int dit) {
        CameraPGEntity entity = cameraMap.get(cameraCode);
        if (null == entity) {
            return null;
        }
        int check = entity.getDirection() + dit;
        String lab = check == 0 ? "x" : "y";
        PoleVertex poleVertex = StrUtil.isEmpty(entity.getPoleCode()) ? null : poleMap.get(entity.getPoleCode() + "_" + lab);

        return null != poleVertex ? poleVertex : StrUtil.isEmpty(entity.getPoleCode()) ? null : poleMap.get(entity.getPoleCode() + "_" + ("y".equals(lab) ? "x" : "y"));
    }

    public PoleVertex getPoleVertexByPoleCode(String poleCode, String lab) {
        return poleMap.get(poleCode + "_" + lab);
    }


    public PoleVertex getPoleVertexByGateNo(String gateNo) {
        GatePGEntity gatePGEntity = gateMap.get(gateNo);
        if (null == gatePGEntity) {
            return null;
        }
        return StrUtil.isEmpty(gatePGEntity.getPoleCode()) ? null : poleMap.get(gatePGEntity.getPoleCode() + "_" + gatePGEntity.getLab());
    }

    public String getCameraCodeByPoleCode(String poleCode) {
        Collection<CameraPGEntity> collections = cameraMap.values();
        return collections.stream().filter(x -> poleCode.equals(x.getPoleCode())).findFirst().map(CameraPGEntity::getCameraCode).orElse(null);
    }

    public GatePGEntity getGatePGEntityByGateNo(String gateNo) {
        return gateMap.get(gateNo);
    }


    public PolePGEntity getPolePGEntityByPoleCode(String poleCode){
        return poleEntityMap.get(poleCode);
    }

}
